const router = require("koa-router")();
const schedule = require("node-schedule");
const _ = require("lodash");
const vm = require("vm");
const axios = require("axios");
const util = require("./../util/util");
const sandbox = require("./../util/sandbox");

router.all(
  "/class/:className/function/:funcName/schedule/start",
  async (ctx, next) => {
    const { className, funcName } = ctx.params;

    const funcInfo = await BaaS.Models.classFunction
      .query({
        where: {
          baas_id: ctx.baas.id,
          class_id: ctx.class.id,
          name: funcName
        }
      })
      .fetch({
        withRedisKey: ctx.getAppKey(`class:${className}:function:${funcName}`)
      });
    if (!funcInfo) {
      throw new Error(`Class ${className} Function ${funcName} Not Found`);
    }
    const scheduleInfo = await BaaS.Models.classSchedule
      .query({
        where: {
          baas_id: ctx.baas.id,
          class_id: ctx.class.id,
          function_id: funcInfo.id
        }
      })
      .fetch({
        withRedisKey: ctx.getAppKey(
          `class:${className}:function:${funcName}:schedule`
        )
      });
    if (!funcInfo) {
      throw new Error(
        `Class ${className} Schedule Function ${funcName} Not Found`
      );
    }

    // 解决schedule已经存在报错
    schedule.cancelJob(
      ctx.getAppKey(`class:${className}:function:${funcName}`, true)
    );

    const job = schedule.scheduleJob(
      ctx.getAppKey(`class:${className}:function:${funcName}`, true),
      scheduleInfo.rule,
      async () => {
        ctx.log(`class:${className}:function:${funcName}:schedule:start`);

        await axios({
          method: ctx.method.toLowerCase(),
          baseURL: `http://127.0.0.1:${ctx.config("port")}`,
          url: `/class/${className}/function/${funcName}`,
          data: ctx.post,
          params: ctx.query,
          headers: ctx.headers
        }).catch(err => {
          ctx.log(`Schedule Error: ${err.message}`);
        });

        ctx.log(`class:${className}:function:${funcName}:schedule:end`);
      }
    );
    ctx.success(`Schedule Function ${funcName} Start`);
  }
);

router.all(
  "/class/:className/function/:funcName/schedule/cancel",
  async (ctx, next) => {
    const { className, funcName } = ctx.params;

    const funcInfo = await BaaS.Models.classFunction
      .query({
        where: {
          baas_id: ctx.baas.id,
          class_id: ctx.class.id,
          name: funcName
        }
      })
      .fetch({
        withRedisKey: ctx.getAppKey(`class:${className}:function:${funcName}`)
      });
    if (!funcInfo) {
      throw new Error(`Class ${className} Function ${funcName} Not Found`);
    }
    const scheduleInfo = await BaaS.Models.classSchedule
      .query({
        where: {
          baas_id: ctx.baas.id,
          class_id: ctx.class.id,
          function_id: funcInfo.id
        }
      })
      .fetch({
        withRedisKey: ctx.getAppKey(
          `class:${className}:function:${funcName}:schedule`
        )
      });
    if (!funcInfo) {
      throw new Error(
        `Class ${className} Schedule Function ${funcName} Not Found`
      );
    }

    schedule.cancelJob(
      ctx.getAppKey(`class:${className}:function:${funcName}`, true)
    );
    ctx.success(`Schedule Function ${funcName} Cancel`);
  }
);

router.all("/class/:className/function/:funcName*", async (ctx, next) => {
  const { className, funcName } = ctx.params;

  const funcInfo = await BaaS.Models.classFunction
    .query({
      where: {
        baas_id: ctx.baas.id,
        class_id: ctx.class.id,
        name: funcName
      }
    })
    .fetch({
      withRedisKey: ctx.getAppKey(`class:${className}:function:${funcName}`)
    });
  if (!funcInfo) {
    throw new Error(`Class ${className} Function ${funcName} Not Found`);
  }

  const key = `lock:class:${className}:function:${funcName}:url:${
    ctx.url
  }:post:${JSON.stringify(ctx.post)}:header:${JSON.stringify(ctx.headers)}`;
  const locked = await BaaS.redis.lock(key);
  if (locked) {
    try {
      await ctx.runInVm(funcInfo.body);
    } catch (err) {
      throw err;
    }

    await BaaS.redis.unlock(key);
  } else {
    console.log(`${key} Waiting`);
  }
});

module.exports = router;
